// Package oracle is the advisor for oracle database.
package oracle

import (
	"github.com/antlr4-go/antlr/v4"
	parser "github.com/bytebase/parser/plsql"

	storepb "github.com/bytebase/bytebase/backend/generated-go/store"
)

// Rule is the interface for Oracle advisor rules.
// Rules implement checking logic without embedding the parser listener.
type Rule interface {
	// OnEnter is called when the parser enters a rule context.
	OnEnter(ctx antlr.ParserRuleContext, nodeType string) error
	// OnExit is called when the parser exits a rule context.
	OnExit(ctx antlr.ParserRuleContext, nodeType string) error
	// Name returns the rule name.
	Name() string
	// GetAdviceList returns the advice list generated by the rule.
	GetAdviceList() ([]*storepb.Advice, error)
}

// BaseRule provides common functionality for Oracle advisor rules.
type BaseRule struct {
	level      storepb.Advice_Status
	title      string
	adviceList []*storepb.Advice
	baseLine   int
}

// NewBaseRule creates a new BaseRule.
func NewBaseRule(level storepb.Advice_Status, title string, baseLine int) BaseRule {
	return BaseRule{
		level:    level,
		title:    title,
		baseLine: baseLine,
	}
}

// AddAdvice adds an advice to the rule.
func (r *BaseRule) AddAdvice(status storepb.Advice_Status, code int32, content string, position *storepb.Position) {
	r.adviceList = append(r.adviceList, &storepb.Advice{
		Status:        status,
		Code:          code,
		Title:         r.title,
		Content:       content,
		StartPosition: position,
	})
}

// GetAdviceList returns the advice list.
func (r *BaseRule) GetAdviceList() ([]*storepb.Advice, error) {
	return r.adviceList, nil
}

// SetBaseLine sets the base line for the rule.
func (r *BaseRule) SetBaseLine(baseLine int) {
	r.baseLine = baseLine
}

// GenericChecker is the only type that embeds BasePlSqlParserListener.
// It dispatches parse tree events to registered rules.
type GenericChecker struct {
	*parser.BasePlSqlParserListener
	rules    []Rule
	baseLine int
}

// NewGenericChecker creates a new GenericChecker with the given rules.
func NewGenericChecker(rules []Rule) *GenericChecker {
	return &GenericChecker{
		BasePlSqlParserListener: &parser.BasePlSqlParserListener{},
		rules:                   rules,
	}
}

// SetBaseLine sets the base line number for error reporting.
func (g *GenericChecker) SetBaseLine(baseLine int) {
	g.baseLine = baseLine
}

// GetBaseLine returns the current base line number.
func (g *GenericChecker) GetBaseLine() int {
	return g.baseLine
}

// EnterEveryRule is called when the parser enters any rule context.
func (g *GenericChecker) EnterEveryRule(ctx antlr.ParserRuleContext) {
	nodeType := g.getNodeType(ctx)
	for _, rule := range g.rules {
		_ = rule.OnEnter(ctx, nodeType)
	}
}

// ExitEveryRule is called when the parser exits any rule context.
func (g *GenericChecker) ExitEveryRule(ctx antlr.ParserRuleContext) {
	nodeType := g.getNodeType(ctx)
	for _, rule := range g.rules {
		_ = rule.OnExit(ctx, nodeType)
	}
}

// GetAdviceList collects and returns all advice from all rules.
func (g *GenericChecker) GetAdviceList() ([]*storepb.Advice, error) {
	var adviceList []*storepb.Advice
	for _, rule := range g.rules {
		list, err := rule.GetAdviceList()
		if err != nil {
			return nil, err
		}
		adviceList = append(adviceList, list...)
	}
	return adviceList, nil
}

// getNodeType returns the node type name for the given context.
func (*GenericChecker) getNodeType(ctx antlr.ParserRuleContext) string {
	// Get the type name of the context
	// For now, we'll handle the most common node types used by Oracle advisors
	switch ctx.(type) {
	case *parser.Create_tableContext:
		return "Create_table"
	case *parser.Alter_tableContext:
		return "Alter_table"
	case *parser.Drop_tableContext:
		return "Drop_table"
	case *parser.Create_indexContext:
		return "Create_index"
	case *parser.Drop_indexContext:
		return "Drop_index"
	case *parser.Insert_statementContext:
		return "Insert_statement"
	case *parser.Update_statementContext:
		return "Update_statement"
	case *parser.Delete_statementContext:
		return "Delete_statement"
	case *parser.Merge_statementContext:
		return "Merge_statement"
	case *parser.Select_statementContext:
		return "Select_statement"
	case *parser.Column_definitionContext:
		return "Column_definition"
	case *parser.Inline_constraintContext:
		return "Inline_constraint"
	case *parser.Out_of_line_constraintContext:
		return "Out_of_line_constraint"
	case *parser.Constraint_clausesContext:
		return "Constraint_clauses"
	case *parser.Drop_primary_key_or_unique_or_generic_clauseContext:
		return "Drop_primary_key_or_unique_or_generic_clause"
	case *parser.Comment_on_columnContext:
		return "Comment_on_column"
	case *parser.Comment_on_tableContext:
		return "Comment_on_table"
	case *parser.Relational_tableContext:
		return "Relational_table"
	case *parser.Relational_propertyContext:
		return "Relational_property"
	case *parser.Table_elementContext:
		return "Table_element"
	case *parser.Unit_statementContext:
		return "Unit_statement"
	case *parser.Sql_scriptContext:
		return "Sql_script"
	case *parser.Column_nameContext:
		return "Column_name"
	case *parser.Table_nameContext:
		return "Table_name"
	case *parser.Tableview_nameContext:
		return "Tableview_name"
	case *parser.IdentifierContext:
		return "Identifier"
	case *parser.Regular_idContext:
		return "Regular_id"
	case *parser.Non_reserved_keywords_pre12cContext:
		return "Non_reserved_keywords_pre12c"
	case *parser.Non_reserved_keywords_in_12cContext:
		return "Non_reserved_keywords_in_12c"
	case *parser.Data_manipulation_language_statementsContext:
		return "Data_manipulation_language_statements"
	case *parser.Add_column_clauseContext:
		return "Add_column_clause"
	case *parser.References_clauseContext:
		return "References_clause"
	case *parser.Modify_col_propertiesContext:
		return "Modify_col_properties"
	case *parser.Insert_into_clauseContext:
		return "Insert_into_clause"
	case *parser.Query_blockContext:
		return "Query_block"
	case *parser.Drop_column_clauseContext:
		return "Drop_column_clause"
	case *parser.DatatypeContext:
		return "Datatype"
	case *parser.Rename_column_clauseContext:
		return "Rename_column_clause"
	case *parser.Selected_listContext:
		return "Selected_list"
	case *parser.Id_expressionContext:
		return "Id_expression"
	case *parser.Table_index_clauseContext:
		return "Table_index_clause"
	case *parser.Alter_table_propertiesContext:
		return "Alter_table_properties"
	case *parser.Compound_expressionContext:
		return "Compound_expression"
	default:
		// Return a generic type name if not found
		return "Unknown"
	}
}
